import axios from 'axios';
import { cloneDeep } from "lodash-es"
import { isFunction } from '@/utils/is'
import { RequestEnum, ContentTypeEnum } from "@/enums/httpEnums"
import { AxiosCanceler } from "./axiosCancel"

export class VAxios {
    constructor(options) {
        this.options = options
        this.axiosInstance = axios.create(options);
        this.setupInterceptors();
    }
    // 初始化拦截器
    setupInterceptors() {
        const transform = this.getTransform();
        if (!transform) {
            return;
        }
        const {
            requestInterceptors,
            requestInterceptorsCatch,
            responseInterceptors,
            responseInterceptorsCatch,
        } = transform;

        const axiosCanceler = new AxiosCanceler();

        // 请求拦截器
        // 请求前处理
        this.axiosInstance.interceptors.request.use(config => {
            const { ignoreCancelToken } = config.requestOptions;
            const ignoreCancel =
                ignoreCancelToken !== undefined
                    ? ignoreCancelToken
                    : this.options.requestOptions?.ignoreCancelToken;
            // 如果配置了忽略重复接口为true，将这个接口添加到pending状态接口的队列中，在接口没有响应前再次重复请求会将前一次pending状态的接口cancel掉
            ignoreCancel && axiosCanceler.addPending(config);

            if (requestInterceptors && isFunction(requestInterceptors)) {
                config = requestInterceptors(config, this.options);
            }
            return config;
        }, undefined)
        // 请求错误处理
        requestInterceptorsCatch &&
            isFunction(requestInterceptorsCatch) &&
            this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);

        // 响应拦截器
        // 接口响应成功处理
        this.axiosInstance.interceptors.response.use(res => {
            // 接口响应完成后将该接口从pending状态接口的队列中移除
            res && axiosCanceler.removePending(res.config);
            if (responseInterceptors && isFunction(responseInterceptors)) {
                res = responseInterceptors(res);
            }
            return res;
        }, undefined);

        // 接口响应错误处理
        responseInterceptorsCatch &&
            isFunction(responseInterceptorsCatch) &&
            this.axiosInstance.interceptors.response.use(undefined, (error) => {
                return responseInterceptorsCatch(this.axiosInstance, error);
            });
    }
    // 获取配置中的数据处理方式
    getTransform() {
        const { transform } = this.options;
        return transform;
    }
    // 上传文件请求
    uploadFile(config, params) {
        const formData = new window.FormData();
        if (params.data) {
            Object.keys(params.data).forEach((key) => {
                const value = params.data[key];
                if (Array.isArray(value)) {
                    value.forEach((item) => {
                        formData.append(`${key}`, item);
                    });
                    return;
                }
                formData.append(key, params.data[key]);
            });
        }
        return this.axiosInstance.request({
            ...config,
            method: 'POST',
            data: formData,
            headers: {
                'Content-type': ContentTypeEnum.FORM_DATA,
                ignoreCancelToken: true,
            },
        });
    }
    // get请求
    get(config, options) {
        return this.request({ ...config, method: RequestEnum.GET }, options)
    }
    // post请求
    post(config, options) {
        return this.request({ ...config, method: RequestEnum.POST }, options)
    }
    // put请求
    put(config, options) {
        return this.request({ ...config, method: RequestEnum.PUT }, options)
    }
    // delete请求
    delete(config, options) {
        return this.request({ ...config, method: RequestEnum.DELETE }, options)
    }
    // patch请求
    patch(config, options) {
        return this.request({ ...config, method: RequestEnum.PATCH }, options)
    }
    // 请求
    request(config, options) {
        let conf = cloneDeep(config);
        const transform = this.getTransform();

        const { requestOptions } = this.options;
        // 合并配置，在调用请求方法时可传入新配置来覆盖默认配置
        const opt = Object.assign({}, requestOptions, options);

        const { beforeRequestHook, transformResponseHook } = transform || {};
        // 请求之前处理config
        if (beforeRequestHook && isFunction(beforeRequestHook)) {
            conf = beforeRequestHook(conf, opt);
        }
        conf.requestOptions = opt;

        return new Promise((resolve, reject) => {
            this.axiosInstance.request(conf).then(res => {
                if (transformResponseHook && isFunction(transformResponseHook)) {
                    try {
                        const ret = transformResponseHook(res, opt);
                        resolve(ret);
                    } catch (err) {
                        reject(err || new Error('request error!'));
                    }
                    return;
                }
                resolve(res);
            }).catch((e) => {
                reject(e);
            })
        })
    }
}
